package controller

import (
	"sems/dao"
	mysql "sems/database"
	"sems/logic"
	"sems/model"
	"sems/util/jwt"
	"sems/util/resp"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func GetEquipments(c *gin.Context) {
	//器材分类名称，器材名称
	categoryName := c.Request.FormValue("category_name")
	equipmentName := c.Request.FormValue("name")
	currentPageStr := c.Request.FormValue("current_page")
	if !IsNumeric(currentPageStr) {
		zap.L().Info("页码输入不合法")
		resp.ResponseErrorWithMsg(c, resp.CodeInvalidParams, "页码输入不合法")
		return
	}
	currentPage, _ := strconv.Atoi(currentPageStr)
	Equipments, list, err := logic.GetEquipments(categoryName, equipmentName, currentPage)
	if err != nil {
		zap.L().Info("查询器材列表出错了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, err.Error())
		return
	}
	// 发送响应
	resp.ResponseSuccess(c, gin.H{
		"equipments": Equipments,
		"page":       list,
	})
}

func GetEquipmentInfo(c *gin.Context) {
	equipmentId, _ := strconv.Atoi(c.Request.FormValue("equipment_id"))
	equipment, err := logic.GetEquipmentInfo(equipmentId)
	if err != nil {
		zap.L().Info("查询器材详情信息出错了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, err.Error())
		return
	}
	// 发送响应
	resp.ResponseSuccess(c, equipment)
}

func BookEquipments(c *gin.Context) {
	equipmentId, _ := strconv.Atoi(c.Request.FormValue("equipment_id"))
	bookCount, _ := strconv.Atoi(c.Request.FormValue("book_count"))
	startDateStr := c.Request.FormValue("start_date")
	endDateStr := c.Request.FormValue("end_date")
	status := c.Request.FormValue("status")
	startDate, err := TimeParse(startDateStr)
	endDate, err := TimeParse(endDateStr)
	if err != nil {
		zap.L().Info("时间解析失败")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "时间解析失败")
		return
	}
	//jwt中获取用户id
	userId, err := jwt.GetUserId(c)
	if err != nil {
		zap.L().Info("解析token失败")
		resp.ResponseError(c, resp.CodeNoAuth)
		return
	}
	if status == "预定" {
		equipment, _ := logic.GetEquipmentInfo(equipmentId)
		//equipmentStatus, err := dao.GetEquipmentSta(equipmentId)
		if err != nil {
			zap.L().Info("查询器材状态失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "查询器材状态失败")
			return
		}
		if equipment.Status == "删除" {
			zap.L().Info("器材已经被下架")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材已经被下架")
			return
		}
		// 检查预定时间范围内的可借数量
		bookings, err := dao.GetEquipmentBookingsByTimeRange(equipmentId, startDate, endDate)
		if err != nil {
			zap.L().Info("获取预定信息失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "获取预定信息失败")
			return
		}
		totalBooked := calculateTotalBooked(bookings)
		availableQuantity := equipment.Quantity - totalBooked
		// 判断可借数量是否满足预定需求
		if availableQuantity <= 0 || availableQuantity < int32(bookCount) {
			zap.L().Info("器材数量不足")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "预定失败，可借数量不足")
			return
		}
		////加锁
		//// 开启数据库事务
		//tx := mysql.Conn.Begin()
		//equipment.Quantity = equipment.Quantity - int32(bookCount)
		//if err = dao.UpdateQuantity(equipmentId, equipment.Quantity); err != nil {
		//	zap.L().Info("器材数量更新失败")
		//	resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材数量更新失败")
		//	tx.Rollback()
		//	return
		//}
		// 记录预定请求
		book := model.ReservationRequest{
			UserID:       userId,
			EquipmentID:  int32(equipmentId),
			RequestCount: int32(bookCount),
			StartTime:    startDate,
			EndTime:      endDate,
			Status:       "预定中",
			RequestTime:  time.Now(),
		}
		if err = dao.BookEquipments(book); err != nil {
			zap.L().Info("器材预定失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材预定失败")
			//tx.Rollback()
			return
		}
		//// 提交事务
		//if err = tx.Commit().Error; err != nil {
		//	zap.L().Error("提交事务失败", zap.Error(err))
		//	resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "服务器忙，请稍后再试")
		//	return
		//}
		//queueName := "booking_success_queue"
		//err = rabbitmq.SendBookingSuccessMessage(rabbitmq.Chan, queueName, userId, equipmentId, startDate, endDate)
		//if err != nil {
		//	log.Fatalf("Error sending booking success message: %s", err)
		//}

		// 发送响应
		resp.ResponseSuccess(c, gin.H{
			"msg": "预定成功，请在预定开始时间之后到店借取器材",
		})
	} else if status == "取消预定" {
		if err = dao.CancelBookEquipments(equipmentId, userId, bookCount, startDate, endDate); err != nil {
			zap.L().Info("用户取消预定器材失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "用户取消预定器材失败")
			return
		}
		resp.ResponseSuccess(c, resp.CodeSuccess)
	}
}

func BorrowEquipments(c *gin.Context) {
	equipmentId, _ := strconv.Atoi(c.Request.FormValue("equipment_id"))
	borrowCount, _ := strconv.Atoi(c.Request.FormValue("borrow_count"))
	startDateStr := c.Request.FormValue("start_date")
	endDateStr := c.Request.FormValue("end_date")
	status := c.Request.FormValue("status")
	startDate, err := TimeParse(startDateStr)
	endDate, err := TimeParse(endDateStr)
	if err != nil {
		zap.L().Info("时间解析失败")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "时间解析失败")
		return
	}
	//jwt中获取用户id
	userId, err := jwt.GetUserId(c)
	if err != nil {
		zap.L().Info("解析token失败")
		resp.ResponseError(c, resp.CodeNoAuth)
		return
	}
	equipment, _ := logic.GetEquipmentInfo(equipmentId)
	if status == "借阅" {
		if equipment.Status == "删除" {
			zap.L().Info("器材已经被下架")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材已经被下架")
			return
		}
		//是否已经预定过
		if dao.IsBookEquipments(equipmentId, userId, borrowCount, startDate, endDate) {
			//equipment, _ := logic.GetEquipmentInfo(equipmentId)
			//加锁，开启数据库事务
			tx := mysql.Conn.Begin()
			equipment.Quantity = equipment.Quantity - int32(borrowCount)
			if err = dao.UpdateQuantity(equipmentId, equipment.Quantity); err != nil {
				zap.L().Info("器材数量更新失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材数量更新失败")
				tx.Rollback()
				return
			}
			if err = dao.CompleteBookEquipments(equipmentId, userId, borrowCount, startDate, endDate); err != nil {
				zap.L().Info("用户取消预定器材失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "用户取消预定器材失败")
				return
			}
			// 记录借阅请求
			borrow := model.BorrowRecord{
				UserID:      userId,
				EquipmentID: equipment.EquipmentID,
				BorrowCount: int32(borrowCount),
				BorrowTime:  startDate,
				ReturnTime:  startDate,
				Status:      "借阅",
			}
			if err = dao.BorrowEquipments(borrow); err != nil {
				zap.L().Info("器材借阅失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材借阅失败")
				tx.Rollback()
				return
			}
			// 提交事务
			if err = tx.Commit().Error; err != nil {
				zap.L().Error("提交事务失败", zap.Error(err))
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "服务器忙，请稍后再试")
				return
			}
			resp.ResponseSuccess(c, resp.CodeSuccess)
		} else {
			//equipment, _ := logic.GetEquipmentInfo(equipmentId)
			// 检查借阅时间范围内的可借数量
			bookings, err := dao.GetEquipmentBookingsByTimeRange(equipmentId, startDate, endDate)
			if err != nil {
				zap.L().Info("获取预定信息失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "获取预定信息失败")
				return
			}
			totalBooked := calculateTotalBooked(bookings)
			availableQuantity := equipment.Quantity - totalBooked
			// 判断可借数量是否满足预定需求
			if availableQuantity <= 0 || availableQuantity < int32(borrowCount) {
				zap.L().Info("器材数量不足")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "借阅失败，可借数量不足")
				return
			}
			//可借
			//加锁，开启数据库事务
			tx := mysql.Conn.Begin()
			equipment.Quantity = equipment.Quantity - int32(borrowCount)
			if err = dao.UpdateQuantity(equipmentId, equipment.Quantity); err != nil {
				zap.L().Info("器材数量更新失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材数量更新失败")
				tx.Rollback()
				return
			}
			// 记录借阅请求
			borrow := model.BorrowRecord{
				UserID:      userId,
				EquipmentID: equipment.EquipmentID,
				Name:        equipment.Name,
				BorrowCount: int32(borrowCount),
				BorrowTime:  startDate,
				ReturnTime:  endDate,
				Status:      "借阅",
			}
			if err = dao.BorrowEquipments(borrow); err != nil {
				zap.L().Info("器材借阅失败")
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材借阅失败")
				tx.Rollback()
				return
			}
			// 提交事务
			if err = tx.Commit().Error; err != nil {
				zap.L().Error("提交事务失败", zap.Error(err))
				resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "服务器忙，请稍后再试")
				return
			}
			resp.ResponseSuccess(c, resp.CodeSuccess)
		}

	} else if status == "归还" {
		//从借阅记录中去归还
		//是否已经归还
		//equipmentStatus, err := dao.GetEquipmentStatus(equipmentId)
		//if err != nil {
		//	zap.L().Info("查询器材状态失败")
		//	resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "查询器材状态失败")
		//	return
		//}
		//if equipmentStatus == "归还" {
		//	zap.L().Info("器材已经归还")
		//	resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材已经归还")
		//	return
		//}
		//加锁，开启数据库事务
		tx := mysql.Conn.Begin()
		equipment.Quantity = equipment.Quantity + int32(borrowCount)
		if err = dao.UpdateQuantity(equipmentId, equipment.Quantity); err != nil {
			zap.L().Info("器材数量更新失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材数量更新失败")
			tx.Rollback()
			return
		}
		if err = dao.ReturnEquipments(equipmentId, userId, borrowCount, startDate, endDate); err != nil {
			zap.L().Info("用户归还器材失败")
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "用户归还器材失败")
			tx.Rollback()
			return
		}
		// 提交事务
		if err = tx.Commit().Error; err != nil {
			zap.L().Error("提交事务失败", zap.Error(err))
			resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "服务器忙，请稍后再试")
			return
		}
		resp.ResponseSuccess(c, resp.CodeSuccess)
	}
}

func GetBorrowRecord(c *gin.Context) {
	equipmentName := c.Request.FormValue("name")
	currentPageStr := c.Request.FormValue("current_page")
	if !IsNumeric(currentPageStr) {
		zap.L().Info("页码输入不合法")
		resp.ResponseErrorWithMsg(c, resp.CodeInvalidParams, "页码输入不合法")
		return
	}
	currentPage, _ := strconv.Atoi(currentPageStr)
	userId, err := jwt.GetUserId(c)
	if err != nil {
		zap.L().Info("解析token失败")
		resp.ResponseError(c, resp.CodeNoAuth)
		return
	}
	Equipments, list, err := logic.GetBorrowRecord(userId, equipmentName, currentPage)
	if err != nil {
		zap.L().Info("查询器材列表出错了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, err.Error())
		return
	}
	// 发送响应
	resp.ResponseSuccess(c, gin.H{
		"equipments": Equipments,
		"page":       list,
	})
}

func DeleteEquipment(c *gin.Context) {
	equipmentId, _ := strconv.Atoi(c.Request.FormValue("equipment_id"))
	if err := dao.DeleteEquipment(equipmentId); err != nil {
		zap.L().Info("删除器材失败了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "删除器材失败了")
		return
	}
	// 发送响应
	resp.ResponseSuccess(c, resp.CodeSuccess)
}

func GetEquipmentCategoryName(c *gin.Context) {
	categoryNames, err := dao.GetEquipmentCategoryName()
	if err != nil {
		zap.L().Info("查询器材分类名失败了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "查询器材分类名失败了")
		return
	}
	// 发送响应
	if len(categoryNames) == 0 {
		resp.ResponseSuccess(c, make([]string, 0))
		return
	}
	resp.ResponseSuccess(c, categoryNames)
}

func AddEquipment(c *gin.Context) {
	quantity, _ := strconv.Atoi(c.Request.FormValue("quantity"))
	equipmentName := c.Request.FormValue("name")
	categoryName := c.Request.FormValue("category_name")
	categoryID, err := dao.GetEquipmentCategoryIdByName(categoryName)
	if err != nil {
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "根据器材名查询器材分类id失败")
		zap.L().Error("根据器材名查询器材分类id失败", zap.Error(err))
		return
	}
	if dao.IsEquipment(equipmentName) {
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "器材名已存在")
		zap.L().Error("器材名已存在", zap.Error(err))
		return
	}
	equipment := model.Equipment{
		Name:         equipmentName,
		Quantity:     int32(quantity),
		Status:       "正常",
		CategoryID:   categoryID,
		CategoryName: categoryName,
		CreateTime:   time.Now(),
	}
	if err = dao.AddEquipment(equipment); err != nil {
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, "添加器材失败")
		zap.L().Error("添加器材失败", zap.Error(err))
		return
	}
	resp.ResponseSuccess(c, equipment)
}

func GetAllBorrowRecord(c *gin.Context) {
	equipmentName := c.Request.FormValue("name")
	currentPageStr := c.Request.FormValue("current_page")
	if !IsNumeric(currentPageStr) {
		zap.L().Info("页码输入不合法")
		resp.ResponseErrorWithMsg(c, resp.CodeInvalidParams, "页码输入不合法")
		return
	}
	currentPage, _ := strconv.Atoi(currentPageStr)
	Equipments, list, err := logic.GetAllBorrowRecord(equipmentName, currentPage)
	if err != nil {
		zap.L().Info("查询器材列表出错了")
		resp.ResponseErrorWithMsg(c, resp.CodeServerBusy, err.Error())
		return
	}
	// 发送响应
	resp.ResponseSuccess(c, gin.H{
		"equipments": Equipments,
		"page":       list,
	})
}
